import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_draw/p10/color_double_tween.dart';

/// 豆人动画效果
// 让豆人的嘴巴开合，也可以让豆人有颜色渐变的动画。可以让它有移动动画、缩放动画、眼睛动一动什么的
class PicMan extends StatefulWidget {
  final Color color;
  final double angle;

  const PicMan({super.key, this.color = Colors.blue, this.angle = 30});

  @override
  State<PicMan> createState() => _PicManState();
}

class _PicManState extends State<PicMan> with SingleTickerProviderStateMixin {
  late AnimationController _animCtrl;
  late Animation<Color?> _colorCtrl;
  late Animation<double> _angleCtrl;

  @override
  void initState() {
    super.initState();
    // lowerBound 是运动的下限，upperBound 是运动的上限
    // 一般 AnimationController 并不设置上下界，否则在使用 Tween 时是在已定的上下界之上变化的。
    // AnimationController 只负责让数字在 0~1 间变化，其余的都交给Animatable来完成。
    // 通过 Animatable#animate(Animation) 可以返回 Animation 对象。
    // 通过 Animation#drive(Animatable) 也可以返回 Animation 对象，两者的作用是一致的。
    // 下面通过 Tween 和 ColorTween 实现如下角度和颜色一起渐变的动画。
    //_animCtrl = AnimationController(vsync: this, lowerBound: 10, upperBound: 40, duration: const Duration(seconds: 1));
    _animCtrl =
        AnimationController(vsync: this, duration: const Duration(seconds: 1));
    // 使用 double 范围 [10,40] 的 Tween 创建动画器
    _angleCtrl = _animCtrl.drive(Tween(begin: 10, end: 40));
    // 使用 color 范围 [Colors.blue,Colors.red] 的 ColorTween 创建动画器
    _colorCtrl =
        ColorTween(begin: Colors.blue, end: Colors.red).animate(_animCtrl);
    _animCtrl.repeat(reverse: true); // 重复执行动画
  }

  @override
  void dispose() {
    _animCtrl.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
        size: const Size(100, 100),
        painter: PicManPainter(
            color: _colorCtrl, angle: _angleCtrl, repaint: _animCtrl));
  }
}

// CustomPainter 中有一个 _repaint 的 Listenable 对象。当监听到这个对象的变化时，画板会触发重绘，这是触发重绘的最高效的方式。
// Animation<T> 和 Animatable<T>
// Animation 是继承自 Listenable , 它是将两个量在一段时间内变化的根源。
// Animatable 本身并不能进行动画，但它可以将Animation对象进行加强。通过 Tween 的子类可以对很多联系的数据进行动画变换，你也可自定义自己Tween 。CurveTween 可以是跳转动画的速率变化，TweenSequence 可以执行一个动画序列。
class PicManPainter extends CustomPainter {
  final Animation<double> repaint;
  final Animation<Color?> color;
  final Animation<double> angle; // 动画角度：与x轴相交的角度

  // 动画方式二：也可以用 Tween#evaluate
  final ColorTween colorTween = ColorTween(begin: Colors.blue, end: Colors.red);
  final Tween<double> angleTween = Tween(begin: 10, end: 40);

  // 动画方式三：自定义Tween
  final ColorDoubleTween mTween = ColorDoubleTween(
      begin: ColorDouble(color: Colors.yellowAccent, value: 10),
      end: ColorDouble(color: Colors.green, value: 40));

  final Paint _paint = Paint();

  PicManPainter(
      {required this.repaint, required this.color, required this.angle})
      : super(repaint: repaint); // <--- 传入 Listenable 可监听对象

  @override
  void paint(Canvas canvas, Size size) {
    canvas.clipRect(Offset.zero & size); // 裁剪画布
    var halfWidth = size.width / 2;
    var halfHeight = size.height / 2;
    // 画布移到中间
    canvas.translate(halfWidth, halfHeight);
    // 取短的做半径
    final double radius = halfWidth > halfHeight ? halfHeight : halfWidth;

    _drawHead(canvas, radius);
    _drawEye(canvas, radius);
  }

  /// 绘制头
  void _drawHead(Canvas canvas, double radius) {
    var rect = Rect.fromCenter(
        center: const Offset(0, 0), width: radius * 2, height: radius * 2);
    //var a = angle.value / 180 * pi; // <---使用动画器的值
    //var a = angleTween.evaluate(repaint) / 180 * pi; // <--- 使用Tween 方式动画
    var a = mTween.evaluate(repaint).value / 180 * pi; // <--- 使用自定义Tween 方式
    //var c = color.value ?? Colors.black ;
    //var c = colorTween.evaluate(repaint) ?? Colors.black;
    var c = mTween.evaluate(repaint).color ?? Colors.black;
    canvas.drawArc(rect, a, 2 * pi - a.abs() * 2, true, _paint..color = c);
  }

  /// 绘制眼睛
  void _drawEye(Canvas canvas, double radius) {
    canvas.drawCircle(Offset(radius * 0.15, -radius * 0.6), radius * 0.12,
        _paint..color = Colors.white);
  }

  @override
  bool shouldRepaint(covariant PicManPainter oldDelegate) =>
      oldDelegate.color != color || oldDelegate.angle != angle;
}
